#!/usr/bin/perl
use strict;
use FindBin;
use File::Basename;
use Getopt::Long;

use ServerAdapter;
use AutoCfg;
use DeployUtils;
use DeployLock;
use BuildUtils;

sub usage {
    my $pname = $FindBin::Script;

    print("Usage: $pname [-v|--verbose 0|1] [--envpath EnvPath] [--version VERSION] [--lang LANG]\n");
    print("              [--md5check 0|1] [--nocheckorgexists 0|1] [--nozip 0|1] [--followtar 0|1]\n");
    print("              ConfigFile1 ConfigFile2\n");
    print("\n");
    print("       --envpath:          example:SYS-A/MODULE-X/SIT\n");
    print("       --version:          version number\n");
    print("       --buildno:          Build number\n");
    print("       --nozip:            just only extract firstlevel package\n");
    print("       --followtar:        extract tar and tar.gz pkg to autocfg\n");
    print("       --md5check:         check md5 sum\n");
    print("       --nocheckorgexists: don't check if the original file exists\n");

    exit(1);
}

sub main {
    my ( $isHelp, $isVerbose, $lang, $envPath, $version, $buildNo );
    my @cfgFiles;
    my $isVerbose        = 0;
    my $noCheckOrgExists = 0;
    my $noZip            = 0;
    my $noclean          = 0;
    my $followTar        = 0;
    my $followZip        = 1;
    my $pureDir          = 0;
    my $doClean          = 1;
    my $checkOrg         = 1;
    my $md5Check         = 0;
    my $charset          = 'utf-8';

    GetOptions(
        'h|help'             => \$isHelp,
        'v|verbose=i'        => \$isVerbose,
        'envpath=s'          => \$envPath,
        'version=s'          => \$version,
        'buildno=s'          => \$buildNo,
        'nozip=i'            => \$noZip,
        'followtar=i'        => \$followTar,
        'puredir=i'          => \$pureDir,
        'md5check=i'         => \$md5Check,
        'lang=s'             => \$lang,
        'nocheckorgexists=i' => \$noCheckOrgExists,
        'noclean=i'          => \$noclean,
        '<>'                 => sub { my $item = shift(@_); push( @cfgFiles, $item ); }
    );

    usage() if ( defined($isHelp) );

    if ( $noZip == 1 ) {
        $followZip = 0;
    }

    if ( $noclean == 1 ) {
        $doClean = 0;
    }

    if ( $noCheckOrgExists == 1 ) {
        $checkOrg = 0;
    }

    if ( defined($lang) ) {
        if ( $lang =~ /\.(.*)$/ ) {
            $charset = lc($1);
        }
        else {
            $charset = lc($lang);
        }
    }

    my $deployUtils = DeployUtils->new();
    my $deployEnv   = $deployUtils->deployInit( $envPath, $version );

    $envPath = $deployEnv->{NAME_PATH};
    $version = $deployEnv->{VERSION};

    my $optionError = 0;
    if ( not defined($envPath) or $envPath eq '' ) {
        $optionError = 1;
        print("ERROR: EnvPath not defined by option --envpath or Environment:NAME_PATH\n");
    }
    if ( not defined($version) or $version eq '' ) {
        $optionError = 1;
        print("ERROR: Version not defined by option --version or Environment:VERSION\n");
    }

    if ( $optionError == 1 ) {
        usage();
    }

    my $namePath = $deployEnv->{NAME_PATH};

    my $serverAdapter = ServerAdapter->new();
    my $verInfo       = $serverAdapter->getEnvVer( $deployEnv, $version );

    my $autoCfgMap = $serverAdapter->getAutoCfgConf($deployEnv);

    my $insCfgMaps = {};
    my $autoCfg    = $autoCfgMap->{autoCfg};
    if ( not defined($autoCfg) ) {
        $autoCfg = {};
    }
    my $insCfgList = $autoCfgMap->{insCfgList};
    if ( not defined( $autoCfgMap->{insCfgList} ) ) {
        $insCfgList = [];
    }
    else {
        my $insNamesMap = {};
        my $insHostsMap = {};
        foreach my $insCfg (@$insCfgList) {
            my $nodeName = lc( $insCfg->{nodeName} );
            $insCfg->{nodeName} = $nodeName;

            my $nodeHost = lc( $insCfg->{host} );
            $insCfg->{host} = $nodeHost;

            my $port = $insCfg->{port};
            if ( not defined($port) ) {
                $port = 0;
                $insCfg->{port} = $port;
            }
            my $nodeUniqName = $nodeHost . '.' . $port;
            $insCfg->{uniqName} = $nodeUniqName;

            my $insAutoCfg = $insCfg->{autoCfg};
            if ( not defined($insAutoCfg) ) {
                $insAutoCfg = {};
                $insCfg->{autoCfg} = $insAutoCfg;
            }

            #$insCfg->{uniqName} = $nodeName;
            $insCfgMaps->{$nodeName}     = $insAutoCfg;
            $insCfgMaps->{$nodeUniqName} = $insAutoCfg;
            $insCfgMaps->{$nodeHost}     = $insAutoCfg;

            if ( defined( $insNamesMap->{$nodeName} ) ) {
                $insNamesMap->{$nodeName} = $insNamesMap->{$nodeName} + 1;
            }
            else {
                $insNamesMap->{$nodeName} = 0;
            }

            if ( defined( $insHostsMap->{$nodeHost} ) ) {
                $insHostsMap->{$nodeHost} = $insHostsMap->{$nodeHost} + 1;
            }
            else {
                $insHostsMap->{$nodeHost} = 0;
            }
        }

        foreach my $insCfg (@$insCfgList) {
            my $insName           = $insCfg->{nodeName};
            my $nameConflictCount = $insNamesMap->{$insName};
            if ( not defined($nameConflictCount) ) {
                $nameConflictCount = 0;
            }
            $insCfg->{nameConflictCount} = $nameConflictCount;

            my $host              = $insCfg->{host};
            my $hostConflictCount = $insHostsMap->{$host};
            if ( not defined($hostConflictCount) ) {
                $hostConflictCount = 0;
            }
            $insCfg->{hostConflictCount} = $hostConflictCount;
        }
    }

    #加工后的autoCfg和insCfgList
    # autoCfg => {
    #     basic    => 'mybasicval',
    #     password => 'mypasswd'
    # },
    # insCfgList => [
    #     {
    #         nodeName          => 'insName1',
    #         uniqName          => '192.168.0.2.8080',
    #         nameConflictCount => 0,
    #         host              => '192.168.0.2',
    #         hostConflictCount => 0,
    #         port              => 8080,
    #         autoCfg           => {
    #             basic    => 'ins1-mybasicval',
    #             password => 'ins1-mypasswd'
    #         }
    #     },
    #     {
    #         nodeName          => 'insName2',
    #         uniqName          => '192.168.0.3.8080',
    #         nameConflictCount => 0,
    #         host              => '192.168.0.3',
    #         hostConflictCount => 0,
    #         port              => 8080,
    #         autoCfg           => {
    #             basic    => 'ins2-mybasicval',
    #             password => 'ins2-mypasswd'
    #         }
    #     }
    # ]

    my $lock      = DeployLock->new($deployEnv);
    my $appLockId = $lock->lockEnvApp($DeployLock::WRITE);
    my $sqlLockId = $lock->lockEnvSql($DeployLock::WRITE);

    END {
        local $?;
        if ( defined($lock) ) {
            $lock->unlockEnvApp($appLockId);
            $lock->unlockEnvSql($sqlLockId);
        }
    }

    $serverAdapter->releaseVerToEnv( $deployEnv, 'releasing' );
    my $buildEnv = $deployUtils->deployInit( $envPath, $version, $verInfo->{buildNo} );

    my $autocfg = AutoCfg->new(
        buildEnv   => $buildEnv,
        autoCfg    => $autoCfg,
        insCfgMaps => $insCfgMaps,
        insCfgList => $insCfgList,
        cfgFiles   => \@cfgFiles,
        version    => $version,
        charset    => $charset,
        followZip  => $followZip,
        doClean    => $doClean,
        followTar  => $followTar,
        checkOrt   => $checkOrg,
        pureDir    => $pureDir,
        md5Check   => $md5Check
    );

    my $hasError = $autocfg->config();

    if ( $hasError != 0 ) {
        $serverAdapter->releaseVerToEnv( $buildEnv, 'release-failed' );
        print("ERROR: Auto config $namePath version:$version failed.\n");
    }
    else {
        print("FINE: Auto config $namePath version:$version success.\n");
    }

    my $keysNotConf = $autocfg->{keysNotConf};
    my @keysToAdd   = keys(%$keysNotConf);
    if (@keysToAdd) {
        $serverAdapter->addAutoCfgKeys( $buildEnv, \@keysToAdd );
        print( 'WARN: Unknown AutoCfg keys:' . join( ',', @keysToAdd ) . " are added to config panel, please go to the config panel and do the configuration.\n" );
    }

    return $hasError;
}

exit main();
